package chat.server.statemachine.election;

import java.io.IOException;

import chat.State;
import chat.statemachine.AbstractContent;
import chat.statemachine.AlgorithmActionInvocationException;
import chat.server.ChatServerElectionState;
import chat.server.ChatServerState;
import chat.server.ChatServerTermin;
import chat.server.statemachine.termin.TerminColor;

public class AlgorithmElectionActions {

	private static boolean debug = false;

	public static Object receiveTokenContent(final State state,
			final AbstractContent content)
	throws AlgorithmActionInvocationException {
		ChatServerState sstate = null;
		if (content == null) {
			throw new IllegalArgumentException(
			"Try executing action with null content");
		}
		if (state == null) {
			throw new IllegalArgumentException(
			"Try executing action with null state");
		}
		if (state instanceof ChatServerState) {
			sstate = (ChatServerState) state;
		} else {
			throw new IllegalArgumentException(
					"Try executing action with state of wrong type ("
					+ state.getClass().getName()
					+ " instead of ChatServerState");
		}
		if (content instanceof ElectionTokenContent) {
			ElectionTokenContent token = (ElectionTokenContent) content;
			if (debug) { printDebugElection("ElectionTokenContent",sstate,token); }
			if (sstate.lrec < sstate.numOfNeigh) {
				if ((sstate.caw == null) || (token.getContent() < sstate.caw)) {
					/* r := token.getContent */
					/* caw == udef OU r < caw */
					sstate.caw = token.getContent();
					sstate.rec = 0;
					sstate.father = token.getSender();
					sstate.fatherKey = sstate.currKey;
					if (debug) { printDebugElection("caw==udef OU r<caw",sstate,token); }
					ElectionTokenContent etc = new ElectionTokenContent(token.getContent(),sstate.identity);
					try {
						if (debug) { System.out.println("Send to All server except one"); }
						sstate.server.sendToAllServersExceptOne(sstate.fatherKey, AlgorithmElection.TOKEN_MESSAGE.getActionIndex(), etc);
					} catch (IOException e) {
						e.printStackTrace();
						throw new AlgorithmActionInvocationException("Error while sendind message");
					}
				}
				if (token.getContent() == sstate.caw){
					/* r == caw */
					sstate.rec++;
					if (sstate.rec == sstate.numOfNeigh) {
						/* rec == #Neigh */
						if (sstate.caw == sstate.identity) {
							/* caw == p */
							if (debug) { printDebugElection("caw == p",sstate,token); }
							ElectionLeaderContent elc = new ElectionLeaderContent(sstate.identity,sstate.identity);
							try {
								if (debug) { System.out.println("Send to all server"); }
								sstate.server.sendToAllServers(AlgorithmElection.LEADER_MESSAGE.getActionIndex(), elc);
							} catch (IOException e) {
								e.printStackTrace();
								throw new AlgorithmActionInvocationException("Error while sendind message");
							}
						}
						else {
							/* caw != p */
							/* sstate.caw != sstate.identity */
							ElectionTokenContent etc2 = new ElectionTokenContent(sstate.caw,sstate.identity);
							if (debug) { printDebugElection("caw != p",sstate,etc2); }
							if (debug) { System.out.println("Send to A server"); }
							try {
								sstate.server.sendToAServer(sstate.fatherKey,AlgorithmElection.TOKEN_MESSAGE.getActionIndex(), etc2);
							} catch (IOException e) {
								e.printStackTrace();
								throw new AlgorithmActionInvocationException("Error while sendind message");
							}
						}
					}
				}
			}
		} else {
			throw new AlgorithmActionInvocationException(
					"Error when executing action: not right message type ("
					+ content.getClass().getName() + ")");
		}
		return null;
	}

	/**
	 * Function to debug election token related content and variable
	 * @param sstate
	 * @param token
	 */
	private static void printDebugElection(String s,ChatServerState sstate,InterfaceElectionContent token) {
		System.out.println("---\n" + s + "\nidentity=" + sstate.identity + ";#Neigh=" + sstate.numOfNeigh + ";from=" + token.getSender() + ";content=" + token.getContent() + ";father=" + sstate.father + ";caw=" + sstate.caw + ";rec=" + sstate.rec + ";lrec=" + sstate.lrec + ";win=" + sstate.win + "\n---");
	}
	/**
	 * Check state of a server to determine if it's the winner or loser
	 * @param sstate the server state object to analyze
	 */
	private static void checkState(ChatServerState sstate) {
		if (sstate.win == null) { return; }
		if (sstate.win == sstate.identity) {
			sstate.state = ChatServerElectionState.Winner;
			sstate.color = TerminColor.BLACK;
			System.out.println("Server " + sstate.identity + " is Winner !");
			
			/* Starting terminate detection thread */
			new Thread(new ChatServerTermin(sstate)).start();
			
			sstate.resetElectionState();
			/* Reset state of server to allow a new wave */
		}
		else {
			sstate.state = ChatServerElectionState.Loser;
			sstate.color = TerminColor.WHITE;
			System.out.println("Server " + sstate.identity + " is Loser !");
			sstate.resetElectionState();
			/* Reset state of server to allow a new wave */
		}
	}
	public static Object receiveLeaderContent(final State state,
			final AbstractContent content)
	throws AlgorithmActionInvocationException {
		ChatServerState sstate = null;
		if (content == null) {
			throw new IllegalArgumentException(
			"Try executing action with null content");
		}
		if (state == null) {
			throw new IllegalArgumentException(
			"Try executing action with null state");
		}
		if (state instanceof ChatServerState) {
			sstate = (ChatServerState) state;
		} else {
			throw new IllegalArgumentException(
					"Try executing action with state of wrong type ("
					+ state.getClass().getName()
					+ " instead of ChatServerState");
		}
		if (content instanceof ElectionLeaderContent) {
			ElectionLeaderContent token = (ElectionLeaderContent) content;
			if (debug) { printDebugElection("ElectionLeaderContent", sstate, token); }
			if (sstate.lrec < sstate.numOfNeigh) {
				if ((sstate.lrec == 0) && (token.getContent() != sstate.identity)) {
					/* lrew == 0 ET r != p */
					//ElectionLeaderContent elc = new ElectionLeaderContent(sstate.caw,sstate.identity);
					ElectionLeaderContent elc = new ElectionLeaderContent(token.getContent(),sstate.identity);
					try {
						sstate.server.sendToAllServers(AlgorithmElection.LEADER_MESSAGE.getActionIndex(), elc);
					} catch (IOException e) {
						e.printStackTrace();
						throw new AlgorithmActionInvocationException("Error while sendind message");
					}
				}
				sstate.lrec++;
				sstate.win = token.getContent();
			}
			if (sstate.lrec >= sstate.numOfNeigh) {
				checkState(sstate);
			}
		} else {
			throw new AlgorithmActionInvocationException(
					"Error when executing action: not right message type ("
					+ content.getClass().getName() + ")");
		}
		return null;
	}
}